use crate::{
    isa::{rtl, CpuOf, IWordOf, Isa, MemoryOf, RtlOf, WordOf},
    memory::{DirMemory, Memory as MemoryTrait},
    utils::in_to_i64,
};

use instr::{Instr, InstrI, InstrJ, InstrS, InstrU};

use tracing::info;

mod cpu;
pub mod gpr;
mod instr;

// 其实 riscv 64 和 32 没必要分开，利用模板有可能无开销的放在一起吗
const MEM_BASE: usize = 0x8000_0000;
type Word = WordOf<Riscv64>;
type IWord = IWordOf<Riscv64>;
type Cpu = CpuOf<Riscv64>;
type Memory = MemoryOf<Riscv64>;
type Rtl = RtlOf<Riscv64>;

fn unsupported_instr() -> ! {
    panic!("unsupported instr")
}

enum Either<A, B> {
    Left(A),
    Right(B),
}

pub struct Riscv64;

impl Riscv64 {
    fn fetch_inst(cpu: &mut Cpu, mem: &Memory) -> Instr {
        let instr = Instr::new(mem.vaddr_get(cpu.pc as usize));
        cpu.pc += 4; // 指令长 32 位
        instr
    }

    fn utype_op_code_to_rtl_op(op: u32) -> rtl::DIOp {
        match op {
            0b0010111 => rtl::DIOp::Aipc,
            0b0110111 => rtl::DIOp::LoadI,
            _ => unsupported_instr(),
        }
    }

    fn jtype_op_code_to_rtl_op(op: u32) -> rtl::DIOp {
        match op {
            0b1101111 => rtl::DIOp::Jal,
            _ => unsupported_instr(),
        }
    }

    fn stype_op_code_to_rtl_op(op: u32, func: u32) -> rtl::SSIOp {
        match op {
            0b0100011 => match func {
                0b000 => rtl::SSIOp::Store1,
                0b001 => rtl::SSIOp::Store2,
                0b010 => rtl::SSIOp::Store4,
                _ => unsupported_instr(),
            },
            _ => unsupported_instr(),
        }
    }

    fn itype_op_code_to_rtl_op(op: u32, func: u32) -> rtl::SDIOp {
        match op {
            0b0000011 => match func {
                0b000 => rtl::SDIOp::Loadi1,
                0b001 => rtl::SDIOp::Loadi2,
                0b010 => rtl::SDIOp::Loadi4,
                0b011 => rtl::SDIOp::Loadi8,
                0b100 => rtl::SDIOp::Loadu1,
                0b101 => rtl::SDIOp::Loadu2,
                0b110 => rtl::SDIOp::Loadu4,
                _ => unsupported_instr(),
            },
            0b0010011 => rtl::SDIOp::Add,
            0b1100111 => rtl::SDIOp::Jal,
            _ => unsupported_instr(),
        }
    }

    fn instr_to_rtl(instr: Instr) -> Rtl {
        info!("instr is {:08x}", instr.value());
        match instr.op_code() {
            0b0010111 /* auipc */ | 0b0110111 /* lui */ => {
                let op = Self::utype_op_code_to_rtl_op(instr.op_code());
                let instru = InstrU::new(instr.value());
                let imm = in_to_i64((instru.simm31_12() << 12) as u64, 32);
                Rtl::DI { rd: instru.rd() as usize, imm, op }
            }
            0b0100011 /* store */ => {
                let instrs = InstrS::new(instr.value());
                let op = Self::stype_op_code_to_rtl_op(instr.op_code(), instrs.funct3());
                let imm = (instrs.simm11_5() << 5) + instrs.imm4_0();
                let imm = in_to_i64(imm as u64, 12);
                Rtl::SSI { rs1: instrs.rs1() as usize, rs2: instrs.rs2() as usize, imm, op }
            }
            0b0000011 /* load */ | 0b0010011 /* addi */ | 0b1100111 /* jalr */ =>  {
                let instri = InstrI::new(instr.value());
                let imm = in_to_i64(instri.simm11_0() as u64, 12);
                let op = Self::itype_op_code_to_rtl_op(instr.op_code(), instri.funct3());
                Rtl::SDI { rs: instri.rs1() as usize, rd: instri.rd() as usize, imm, op }
            }
            0b1101111 /* jal */ => {
                let instrj = InstrJ::new(instr.value());
                let imm = (instrj.simm20() << 20)
                    + (instrj.imm12_19() << 12)
                    + (instrj.imm11() << 11)
                    + (instrj.imm1_10() << 1);
                let imm = in_to_i64(imm as u64, 21);
                let op = Self::jtype_op_code_to_rtl_op(instr.op_code());
                Rtl::DI { rd: instrj.rd() as usize, imm, op }
            }
            0b0011011 => unimplemented!("addiw"),
            0b1101011 => { Rtl::NoArg(rtl::NoArgOp::Trap)  }, // nemu trap
            0b0 => {
                // 不正常的结束
                // error!("op code is 0, load error addr");
                panic!("op code is 0, load error addr");
            }
            _ => {
                unimplemented!("unsupported instr, opcode: {:07b}", instr.op_code());
            }
        }
    }
}

impl Isa for Riscv64 {
    type Word = u64;
    type Memory = DirMemory<MEM_BASE>;
    type Cpu = cpu::Riscv64Cpu;
    const GPR_NUMBER: usize = 32;
    const PROG_START: Word = 0x1000 + Self::Memory::BASE as Word;

    fn exec(cpu: &mut Self::Cpu, mem: &mut Self::Memory, n: usize) {
        for _ in 0..n {
            let instr = Self::fetch_inst(cpu, mem);
            rtl::exec::<Self>(cpu, mem, Self::instr_to_rtl(instr))
        }
    }
}

#[cfg(test)]
mod test {}
